#include <stdio.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/syscall.h>

#include "WorkSvr.h"
#include "SvrMgr.h"

// #define SMALL_BLOCK_LENGTH_MAX 1500
// #define SMALL_BLOCK_SIZE_LEVELS 20
#define IPC_BUFFER_MAX 256
#define gettid() syscall(SYS_gettid)

enum class IpcCmd
{
    Accept = 1,
    StopSvr
};

struct AcceptData
{
    int icfd;
    struct sockaddr sa;
    socklen_t sl;
};

struct IpcAccept
{
    IpcCmd cmd;
    AcceptData ad;
};

CWorkSvr::CWorkSvr()
{
}

CWorkSvr::~CWorkSvr()
{
    Destroy();
}

void *CWorkSvr::operator new(std::size_t sz)
{
    void *pRet = nullptr;
    CMemPool *mp = CMemPool::GetInstance();

    if (nullptr != mp)
        pRet = mp->Alloc(sz);

    return pRet;
}

void CWorkSvr::operator delete(void *ptr)
{
    CMemPool *mp = CMemPool::GetInstance();

    if ((nullptr == mp) || (nullptr == ptr))
        return;

    mp->Free(ptr);
}

bool CWorkSvr::Init(int ilfd)
{
    bool bRet = CServer::Init(ilfd);

    m_bufIpcRecv.SetSocket(m_irspfd);
    m_bufIpcSend.SetSocket(m_iwspfd);
    m_pbIcp.Init(IPC_BUFFER_MAX);
    m_lstClnt.SetExtData(this);

    return bRet;
}

bool CWorkSvr::Destroy()
{
    CServer::Destroy();
    m_lstClnt.Destroy();
    m_pbIcp.Destroy();
    m_bufIpcRecv.Destroy();
    m_bufIpcSend.Destroy();

    return true;
}

bool CWorkSvr::Stop()
{
    // CServer::Stop();
    IpcCmd cmd = IpcCmd::StopSvr;
    IpcSend(&cmd, sizeof(cmd));

    return true;
}

bool CWorkSvr::ErrorEvent(epoll_event &ee)
{
    if ((&m_ilfd == ee.data.ptr) || (&m_irspfd == ee.data.ptr) || (&m_iwspfd == ee.data.ptr))
    {
        // 退出进程
        kill(0, SIGINT);
    }
    else if (nullptr != ee.data.ptr)
    {
        // 断开连接，删除客户端
        DestroyClientNode((ClientNode *)ee.data.ptr);
    }

    return true;
}

bool CWorkSvr::InputEvent(epoll_event &ee)
{
    bool bRet = false;

    if (&m_ilfd == ee.data.ptr)
    {
        bRet = AcceptEvent();
    }
    else if (&m_irspfd == ee.data.ptr)
    {
        printf("InputEvent\ttid:%ld\n", gettid());
        bRet = IpcRecvEvent();
    }
    else if (nullptr != ee.data.ptr)
    {
        bRet = RecvHandle((ClientNode *)ee.data.ptr);
    }

    return bRet;
}

bool CWorkSvr::OutputEvent(epoll_event &ee)
{
    bool bRet = false;

    if (&m_iwspfd == ee.data.ptr)
    {
        bRet = IpcSendEvent();
    }
    else if (nullptr != ee.data.ptr)
    {
        bRet = SendHandler((ClientNode *)ee.data.ptr);
    }

    return bRet;
}

bool CWorkSvr::AcceptEvent()
{
    int icfd;
    struct sockaddr sa;
    socklen_t sl = sizeof(sockaddr);

    while (true)
    {
        icfd = accept(m_ilfd, &sa, &sl);

        if (-1 == icfd)
        {
            if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
                break;
            else
            {
                printf("Error accept:\terror=%d\tfile=%s\tline=%d\n", errno, __FILE__, __LINE__);
                return false;
            }
        }
        else
        {
            if (!SetNonBlock(icfd))
            {
                printf("Error (!SetNonBlock(icfd)):\terror=%d\tfile=%s\tline=%d\n", errno, __FILE__, __LINE__);
                return false;
            }

            // 获得bind客户端最少的服务
            // 向该服务发送信息
            if (!AcceptProc(icfd, sa, sl))
            {
                printf("Error AcceptProc:\terror=%d\tfile=%s\tline=%d\n", errno, __FILE__, __LINE__);
                return false;
            }
        }
    }

    return true;
}

bool CWorkSvr::IpcRecvEvent()
{
    int iRecv = m_bufIpcRecv.Recv();

    if (0 < iRecv)
    {
        while (IpcDataParse())
            ;
    }

    return true;
}

bool CWorkSvr::IpcSendEvent()
{
    int iLen = m_bufIpcSend.GetDataLength();

    if (0 < iLen)
        m_bufIpcSend.Send();

    return true;
}

bool CWorkSvr::RecvHandle(ClientNode *pNode)
{
    int iRecv = 0;
    CClient *pClnt = nullptr;

    if (nullptr == pNode)
        return false;

    pClnt = pNode->pClnt;

    if (nullptr == pClnt)
        return false;

    iRecv = pClnt->Recv();

    if (0 < iRecv)
    {
        //========测试========
        CPkgBuf pb;
        pb.Init(1024);
        int iRead = pClnt->Read(pb.GetWritePos(), iRecv);
        pClnt->Send(pb.GetData(), iRead);
        pb.Destroy();
        //========测试========
    }
    else if (-1 == iRecv)
    {
        DestroyClientNode(pNode);
    }

    return true;
}

bool CWorkSvr::SendHandler(ClientNode *pNode)
{
    int iRecv = 0;
    CClient *pClnt = nullptr;

    if (nullptr == pNode)
        return false;

    pClnt = pNode->pClnt;

    if (nullptr == pClnt)
        return false;

    if (pClnt->HasNeedSendData())
        pClnt->Send();

    return true;
}

int CWorkSvr::IpcSend(void *pSrc, int iLen)
{
    return m_bufIpcSend.Send(pSrc, iLen);
}

bool CWorkSvr::AcceptProc(int icfd, sockaddr &sa, socklen_t sl)
{
    bool bRet = false;
    CServer *pSvr = nullptr;
    CSvrMgr *pSvrMgr = CSvrMgr::GetInstance();

    if (nullptr == pSvrMgr)
        return false;

    if (0 == m_lstClnt.GetSize())
    {
        printf("Local server-1 AcceptProc\ticfd=%d\ttid:%ld\n", icfd, gettid());
        bRet = AcceptHandle(icfd, sa, sl);
    }
    else
    {
        pSvr = pSvrMgr->GetAcceptServer();

        if (nullptr == pSvr)
            return false;

        if (this == pSvr)
        {
            printf("Local server-2 AcceptProc\ticfd=%d\ttid:%ld\n", icfd, gettid());
            bRet = AcceptHandle(icfd, sa, sl);
        }
        else
        {
            int iRet = 0;
            IpcAccept IpcAcc;
            int iSize = (int)sizeof(IpcAcc);
            IpcAcc.cmd = IpcCmd::Accept;
            IpcAcc.ad.icfd = icfd;
            IpcAcc.ad.sa = sa;
            IpcAcc.ad.sl = sl;
            iRet = pSvr->IpcSend(&IpcAcc, iSize);
            printf("Other server AcceptProc\ticfd=%d\ttid:%ld\n", icfd, gettid());

            if (iRet >= iSize)
                bRet = true;
        }
    }

    return bRet;
}

bool CWorkSvr::AcceptHandle(int icfd, sockaddr &sa, socklen_t sl)
{
    printf("AcceptHandle\ticfd=%d\ttid:%ld\n", icfd, gettid());
    CreateClientNode(icfd);
    return true;
}

bool CWorkSvr::IpcDataParse()
{
    bool bRet = false;
    AcceptData *pad = nullptr;
    uint8_t *pkg = m_pbIcp.GetData();
    uint32_t nSize = m_pbIcp.GetLength();
    int iLen = m_bufIpcRecv.GetDataLength();
    int iCmdSize = (int)sizeof(IpcCmd);

    if (iCmdSize > (int)nSize)
    {
        if (iCmdSize > iLen)
            return false;

        m_bufIpcRecv.Read(m_pbIcp.GetWritePos(), iCmdSize);
        m_pbIcp.SetLength(iCmdSize);
    }

    switch (*(IpcCmd *)pkg)
    {
    case IpcCmd::Accept:
        iLen = m_bufIpcRecv.GetDataLength();
        nSize = m_pbIcp.GetLength();

        if (sizeof(IpcAccept) > nSize)
        {
            if ((int)sizeof(AcceptData) > iLen)
                return false;

            m_bufIpcRecv.Read(m_pbIcp.GetWritePos(), sizeof(AcceptData));
            m_pbIcp.SetLength(sizeof(IpcAccept));
        }

        pad = (AcceptData *)(pkg + iCmdSize);
        AcceptHandle(pad->icfd, pad->sa, pad->sl);
        m_pbIcp.SetLength(0);
        iLen = m_bufIpcRecv.GetDataLength();
        bRet = (iLen > 0);
        break;

    case IpcCmd::StopSvr:
        CServer::Stop();
        break;

    default:
        printf("Error IpcCmd:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        break;
    }

    return bRet;
}

ClientNode *CWorkSvr::CreateClientNode(int ifd)
{
    bool bRet = false;
    ClientNode *pNode = m_lstClnt.NewNode();

    if (nullptr == pNode)
        return nullptr;

    if (!pNode->NewClient(ifd))
    {
        m_lstClnt.DeleteNode(pNode);
        return nullptr;
    }

    pNode->pClnt->SetBelongNode(pNode);
    bRet = m_lstClnt.PushBack(pNode);

    if (!bRet)
    {
        m_lstClnt.DeleteNode(pNode);
        return nullptr;
    }

    UpdateEvents(ifd, EPOLLIN | EPOLLOUT | EPOLLET, EPOLL_CTL_ADD, pNode);

    return pNode;
}

void CWorkSvr::DestroyClientNode(ClientNode *pNode)
{
    int ifd = -1;

    if (nullptr == pNode)
        return;

    if (nullptr == pNode->pClnt)
        return;

    ifd = pNode->pClnt->GetSocketDescriptor();
    UpdateEvents(ifd, 0, EPOLL_CTL_DEL, nullptr);
    m_lstClnt.Remove(pNode);
    m_lstClnt.DeleteNode(pNode);
}